<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>第 3 章 -- Array的变化侦测</title>
</head>
<body>
    <script>
        function defineReactive(data, key, val) {
            if (typeof val === 'object') {
                new Observer(val);
            }
            let dep = new Dep();
            Object.defineProperty(data, key, {
                enumerable: true,
                configurable: true,
                get: function() {
                    dep.depend();
                    return val;
                },
                set: function(newVal){
                    if (val === newVal) {
                        return;
                    }
                    val = newVal;
                    dep.notify();
                }
            })
        }
        class Observer {
            constructor() {
                this.subs = [];
            }
            addSub(sub) {
                this.subs.push(sub);
            }
            removeSub(sub) {
                remove(this.subs, sub);
            }
            depend() {
                if (window.target) {
                    this.addSub(window.target);
                }
            }
            notify() {
                const subs = this.subs.slice();
                for (let i = 0, l = subs.length; i < l; i++) {
                    subs[i].update();
                }
            }
        }
        function remove(arr, item) {
            if (arr.length) {
                const index = arr.indexOf(item);
                if (index > -1) {
                    return arr.splice(index, 1);
                }
            }
        }
        class Watcher {
            constructor(vm, expOrFn, cb) {
                this.vm = vm;
                this.getter = parsePath(expOrFn);
                this.cb = cb;
                this.value = this.get();
            }
            get() {
                window.target = this;
                let value = this.getter.call(this.vm, this.vm);
                window.target = undefined;
                return value;
            }
            update() {
                const oldValue = this.value;
                this.value = this.get();
                this.cb.call(this.vm, this.value, oldValue);
            }
        }
        const bailRE = /[^\w.$]/;
        function parsePath(path) {
            if (bailRE.test(path)) {
                return;
            }
            const segments = path.split('.');
            return function(obj) {
                for (let i = 0; i < segments.length; i++) {
                    if (!obj) {
                        return;
                    }
                    obj = obj[segments[i]];
                }
                return obj;
            }
        }


        // - 调用示例
        function MVVM(options = {}) {
            this.$options = options;
            let data = this._data = this.$options.data;

            new Observer(data);
            Object.keys(data).forEach((key) => {
                if (data.hasOwnProperty(key)) {
                    new Watcher(data, key, (newVale, oldVale) => {
                        console.log('新值返回: ', newValue);
                        console.log('旧值返回: ', oldValue);
                    })
                }
            })
        }
        let vm = new MVVM({
            el: '#app',
            data: {
                a: {aa: 'I am aa'},
                b: {name: '我是 b 的 name 值'},
                c: '我是 c 属性!'
            }
        });

        // - 在浏览器的控制台中输出下面内容测试:
        // vm._data;
        // vm_data.a;
        // vm_data.a.aa;
        // vm_data.a.aa = "I changed aa's value";
        // vm_data.a.aa;
    </script>
</body>
</html>